package org.bdware.doip.implementation;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.QueryBuilder;
import org.apache.lucene.util.Version;
import org.bdware.doip.core.model.digitalObject.DigitalObject;
import org.bdware.doip.core.model.metadata.SearchParameter;
import org.bdware.doip.core.model.metadata.SearchResult;
import org.bdware.doip.core.utils.DoipGson;

import java.io.IOException;
import java.nio.file.Paths;

public class DoIndexerLuceneImpl implements DoIndexer{
    Logger logger = Logger.getLogger(DoIndexerLuceneImpl.class);
    public Directory directory;
    public Analyzer analyzer;
    public IndexWriter indexWriter;
    public String indexPath;

    public DoIndexerLuceneImpl(){
        this("./doIndex/");
    }

    public DoIndexerLuceneImpl(String indexPath) {
        try {
            logger.info("index path: " + indexPath);
            this.indexPath = indexPath;
            directory = FSDirectory.open(Paths.get(indexPath));
            analyzer = new CNAnalyzer(true);
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
            indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
            indexWriter = new IndexWriter(directory, indexWriterConfig);
            indexWriter.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean indexDigitalObject(DigitalObject doc) throws IOException {
        String json = DoipGson.getDoipGson().toJson(doc);
        return indexJson(doc.id, json);
    }

    @Override
    public boolean indexJson(String handle, String doc) throws IOException {
        Document document = new Document();
        Field stringId = new StringField("id", handle, Store.YES);
        Field stringCont = new TextField("content", doc, Store.YES);
        document.add(stringId);
        document.add(stringCont);
        indexWriter.updateDocument(new Term("id", handle), document);
        indexWriter.commit();
        return true;
    }

    @Override
    public SearchResult search(SearchParameter searchParameter) throws IOException {
        String keyword = searchParameter.query;
        int totalSize = searchParameter.pageSize;
        DirectoryReader directoryReader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
        QueryParser parser = new QueryParser("content", analyzer);
        Query query = null;
        try {
            query = parser.parse(keyword);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ScoreDoc[] hits = indexSearcher.search(query, totalSize).scoreDocs;
        for(int i =1;i<searchParameter.pageNum;i++){
            if(hits.length < totalSize) break;
            ScoreDoc taildoc = hits[hits.length-1];
            hits = indexSearcher.searchAfter(taildoc,query,totalSize).scoreDocs;
        }
        SearchResult searchResult = new SearchResult();
        searchResult.size = hits.length;
        for (int i = 0; i < hits.length; i++) {
            Document hitDoc = indexSearcher.doc(hits[i].doc);
            if (searchParameter.type.equals("full")) {
                JsonObject jsonObject = JsonParser.parseString(hitDoc.get("content")).getAsJsonObject();
                jsonObject.addProperty("doID",hitDoc.get("id"));
                searchResult.addItem(jsonObject);
            } else {
                searchResult.results.add(hitDoc.get("id"));
            }
        }
        return searchResult;
    }

    @Override
    public boolean deleteDigitalObject(String id) {
        try {
            indexWriter.deleteDocuments(new Term("id", id));
            indexWriter.commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
